
package ibxm;

public class Sample {
    public String name;
    public boolean set_panning;
    public int volume, panning;
    public int transpose;

    private int loop_start, loop_length;
    private short[] sample_data;

    /* For the sinc interpolator.*/
    private static final int POINT_SHIFT = 4;
    private static final int POINTS = 1 << POINT_SHIFT;
    private static final int OVERLAP = POINTS >> 1;
    private static final int INTERP_SHIFT = IBXM.FP_SHIFT - 4;
    private static final int INTERP_BITMASK = (1 << INTERP_SHIFT) - 1;
    private static final short[] sinc_table = {
            0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0, 0,
            0, 0, -5, 36, -142, 450, -1439, 32224, 2302, -974, 455, -190, 64, -15, 2, 0,
            0, 6, -33, 128, -391, 1042, -2894, 31584, 4540, -1765, 786, -318, 105, -25, 3, 0,
            0, 10, -55, 204, -597, 1533, -4056, 30535, 6977, -2573, 1121, -449, 148, -36, 5, 0,
            -1, 13, -71, 261, -757, 1916, -4922, 29105, 9568, -3366, 1448, -578, 191, -47, 7, 0,
            -1, 15, -81, 300, -870, 2185, -5498, 27328, 12263, -4109, 1749, -698, 232, -58, 9, 0,
            -1, 15, -86, 322, -936, 2343, -5800, 25249, 15006, -4765, 2011, -802, 269, -68, 10, 0,
            -1, 15, -87, 328, -957, 2394, -5849, 22920, 17738, -5298, 2215, -885, 299, -77, 12, 0,
            0, 14, -83, 319, -938, 2347, -5671, 20396, 20396, -5671, 2347, -938, 319, -83, 14, 0,
            0, 12, -77, 299, -885, 2215, -5298, 17738, 22920, -5849, 2394, -957, 328, -87, 15, -1,
            0, 10, -68, 269, -802, 2011, -4765, 15006, 25249, -5800, 2343, -936, 322, -86, 15, -1,
            0, 9, -58, 232, -698, 1749, -4109, 12263, 27328, -5498, 2185, -870, 300, -81, 15, -1,
            0, 7, -47, 191, -578, 1448, -3366, 9568, 29105, -4922, 1916, -757, 261, -71, 13, -1,
            0, 5, -36, 148, -449, 1121, -2573, 6977, 30535, -4056, 1533, -597, 204, -55, 10, 0,
            0, 3, -25, 105, -318, 786, -1765, 4540, 31584, -2894, 1042, -391, 128, -33, 6, 0,
            0, 2, -15, 64, -190, 455, -974, 2302, 32224, -1439, 450, -142, 36, -5, 0, 0,
            0, 0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0
    };

    public Sample() {
        name = "";
        set_sample_data(new short[0], 0, 0, false);
    }

    public void set_sample_data(short[] data, int loop_start, int loop_length, boolean ping_pong) {
        int offset;
        short sample;
        if (loop_start < 0) {
            loop_start = 0;
        }
        if (loop_start >= data.length) {
            loop_start = data.length - 1;
        }
        if (loop_start + loop_length > data.length) {
            loop_length = data.length - loop_start;
        }
        if (loop_length <= 1) {
            sample_data = new short[OVERLAP + data.length + OVERLAP * 3];
            System.arraycopy(data, 0, sample_data, OVERLAP, data.length);
            offset = 0;
            while (offset < OVERLAP) {
                sample = sample_data[OVERLAP + data.length - 1];
                sample = (short) (sample * (OVERLAP - offset) / OVERLAP);
                sample_data[OVERLAP + data.length + offset] = sample;
                offset += 1;
            }
            loop_start = OVERLAP + data.length + OVERLAP;
            loop_length = 1;
        } else {
            if (ping_pong) {
                sample_data = new short[OVERLAP + loop_start + loop_length * 2 + OVERLAP * 2];
                System.arraycopy(data, 0, sample_data, OVERLAP, loop_start + loop_length);
                offset = 0;
                while (offset < loop_length) {
                    sample = data[loop_start + loop_length - offset - 1];
                    sample_data[OVERLAP + loop_start + loop_length + offset] = sample;
                    offset += 1;
                }
                loop_start = loop_start + OVERLAP;
                loop_length = loop_length * 2;
            } else {
                sample_data = new short[OVERLAP + loop_start + loop_length + OVERLAP * 2];
                System.arraycopy(data, 0, sample_data, OVERLAP, loop_start + loop_length);
                loop_start = loop_start + OVERLAP;
            }
            offset = 0;
            while (offset < OVERLAP * 2) {
                sample = sample_data[loop_start + offset];
                sample_data[loop_start + loop_length + offset] = sample;
                offset += 1;
            }
        }
        this.loop_start = loop_start;
        this.loop_length = loop_length;
    }

    public void resample_nearest(
            int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
            int[] mix_buffer, int frame_offset, int frames) {
        int loop_end, offset, end, max_sample_idx;
        sample_idx += OVERLAP;
        loop_end = loop_start + loop_length - 1;
        offset = frame_offset << 1;
        end = (frame_offset + frames - 1) << 1;
        while (frames > 0) {
            if (sample_idx > loop_end) {
                if (loop_length <= 1) {
                    break;
                }
                sample_idx = loop_start + (sample_idx - loop_start) % loop_length;
            }
            max_sample_idx = sample_idx + ((sample_frac + (frames - 1) * step) >> IBXM.FP_SHIFT);
            if (max_sample_idx > loop_end) {
                while (sample_idx <= loop_end) {
                    mix_buffer[offset++] += sample_data[sample_idx] * left_gain >> IBXM.FP_SHIFT;
                    mix_buffer[offset++] += sample_data[sample_idx] * right_gain >> IBXM.FP_SHIFT;
                    sample_frac += step;
                    sample_idx += sample_frac >> IBXM.FP_SHIFT;
                    sample_frac &= IBXM.FP_MASK;
                }
            } else {
                while (offset <= end) {
                    mix_buffer[offset++] += sample_data[sample_idx] * left_gain >> IBXM.FP_SHIFT;
                    mix_buffer[offset++] += sample_data[sample_idx] * right_gain >> IBXM.FP_SHIFT;
                    sample_frac += step;
                    sample_idx += sample_frac >> IBXM.FP_SHIFT;
                    sample_frac &= IBXM.FP_MASK;
                }
            }
            frames = (end - offset + 2) >> 1;
        }
    }

    public void resample_linear(
            int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
            int[] mix_buffer, int frame_offset, int frames) {
        int loop_end, offset, end, max_sample_idx, amplitude;
        sample_idx += OVERLAP;
        loop_end = loop_start + loop_length - 1;
        offset = frame_offset << 1;
        end = (frame_offset + frames - 1) << 1;
        while (frames > 0) {
            if (sample_idx > loop_end) {
                if (loop_length <= 1) {
                    break;
                }
                sample_idx = loop_start + (sample_idx - loop_start) % loop_length;
            }
            max_sample_idx = sample_idx + ((sample_frac + (frames - 1) * step) >> IBXM.FP_SHIFT);
            if (max_sample_idx > loop_end) {
                while (sample_idx <= loop_end) {
                    amplitude = sample_data[sample_idx];
                    amplitude += (sample_data[sample_idx + 1] - amplitude) * sample_frac >> IBXM.FP_SHIFT;
                    mix_buffer[offset++] += amplitude * left_gain >> IBXM.FP_SHIFT;
                    mix_buffer[offset++] += amplitude * right_gain >> IBXM.FP_SHIFT;
                    sample_frac += step;
                    sample_idx += sample_frac >> IBXM.FP_SHIFT;
                    sample_frac &= IBXM.FP_MASK;
                }
            } else {
                while (offset <= end) {
                    amplitude = sample_data[sample_idx];
                    amplitude += (sample_data[sample_idx + 1] - amplitude) * sample_frac >> IBXM.FP_SHIFT;
                    mix_buffer[offset++] += amplitude * left_gain >> IBXM.FP_SHIFT;
                    mix_buffer[offset++] += amplitude * right_gain >> IBXM.FP_SHIFT;
                    sample_frac += step;
                    sample_idx += sample_frac >> IBXM.FP_SHIFT;
                    sample_frac &= IBXM.FP_MASK;
                }
            }
            frames = (end - offset + 2) >> 1;
        }
    }

    public void resample_sinc(
            int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
            int[] mix_buffer, int frame_offset, int frames) {
        int offset, end, loop_end, table_idx, a1, a2, amplitude;
        loop_end = loop_start + loop_length - 1;
        offset = frame_offset << 1;
        end = (frame_offset + frames - 1) << 1;
        while (offset <= end) {
            if (sample_idx > loop_end) {
                if (loop_length <= 1) {
                    break;
                }
                sample_idx = loop_start + (sample_idx - loop_start) % loop_length;
            }
            table_idx = (sample_frac >> INTERP_SHIFT) << POINT_SHIFT;
            a1 = sinc_table[table_idx + 0] * sample_data[sample_idx + 0] >> 15;
            a1 += sinc_table[table_idx + 1] * sample_data[sample_idx + 1] >> 15;
            a1 += sinc_table[table_idx + 2] * sample_data[sample_idx + 2] >> 15;
            a1 += sinc_table[table_idx + 3] * sample_data[sample_idx + 3] >> 15;
            a1 += sinc_table[table_idx + 4] * sample_data[sample_idx + 4] >> 15;
            a1 += sinc_table[table_idx + 5] * sample_data[sample_idx + 5] >> 15;
            a1 += sinc_table[table_idx + 6] * sample_data[sample_idx + 6] >> 15;
            a1 += sinc_table[table_idx + 7] * sample_data[sample_idx + 7] >> 15;
            a1 += sinc_table[table_idx + 8] * sample_data[sample_idx + 8] >> 15;
            a1 += sinc_table[table_idx + 9] * sample_data[sample_idx + 9] >> 15;
            a1 += sinc_table[table_idx + 10] * sample_data[sample_idx + 10] >> 15;
            a1 += sinc_table[table_idx + 11] * sample_data[sample_idx + 11] >> 15;
            a1 += sinc_table[table_idx + 12] * sample_data[sample_idx + 12] >> 15;
            a1 += sinc_table[table_idx + 13] * sample_data[sample_idx + 13] >> 15;
            a1 += sinc_table[table_idx + 14] * sample_data[sample_idx + 14] >> 15;
            a1 += sinc_table[table_idx + 15] * sample_data[sample_idx + 15] >> 15;
            a2 = sinc_table[table_idx + 16] * sample_data[sample_idx + 0] >> 15;
            a2 += sinc_table[table_idx + 17] * sample_data[sample_idx + 1] >> 15;
            a2 += sinc_table[table_idx + 18] * sample_data[sample_idx + 2] >> 15;
            a2 += sinc_table[table_idx + 19] * sample_data[sample_idx + 3] >> 15;
            a2 += sinc_table[table_idx + 20] * sample_data[sample_idx + 4] >> 15;
            a2 += sinc_table[table_idx + 21] * sample_data[sample_idx + 5] >> 15;
            a2 += sinc_table[table_idx + 22] * sample_data[sample_idx + 6] >> 15;
            a2 += sinc_table[table_idx + 23] * sample_data[sample_idx + 7] >> 15;
            a2 += sinc_table[table_idx + 24] * sample_data[sample_idx + 8] >> 15;
            a2 += sinc_table[table_idx + 25] * sample_data[sample_idx + 9] >> 15;
            a2 += sinc_table[table_idx + 26] * sample_data[sample_idx + 10] >> 15;
            a2 += sinc_table[table_idx + 27] * sample_data[sample_idx + 11] >> 15;
            a2 += sinc_table[table_idx + 28] * sample_data[sample_idx + 12] >> 15;
            a2 += sinc_table[table_idx + 29] * sample_data[sample_idx + 13] >> 15;
            a2 += sinc_table[table_idx + 30] * sample_data[sample_idx + 14] >> 15;
            a2 += sinc_table[table_idx + 31] * sample_data[sample_idx + 15] >> 15;
            amplitude = a1 + ((a2 - a1) * (sample_frac & INTERP_BITMASK) >> INTERP_SHIFT);
            mix_buffer[offset] += amplitude * left_gain >> IBXM.FP_SHIFT;
            mix_buffer[offset + 1] += amplitude * right_gain >> IBXM.FP_SHIFT;
            offset += 2;
            sample_frac += step;
            sample_idx += sample_frac >> IBXM.FP_SHIFT;
            sample_frac &= IBXM.FP_MASK;
        }
    }

    public boolean has_finished(int sample_idx) {
        boolean finished;
        finished = false;
        if (loop_length <= 1 && sample_idx > loop_start) {
            finished = true;
        }
        return finished;
    }
}
